#include "event.h"
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
using namespace boost;

Event::Event(Event::Type type, int number, int instNum, int val, 
	     int effect, int effectArg, int index)
: m_type(type), m_number(number), m_instNum(instNum), m_val(val), 
  m_effect(effect), m_effectArg(effectArg), m_index(index) {
}

Event::~Event() {
}

Event::Type Event::type() {
     return m_type;
}

void Event::setEvent(int num, int inst, int val) {
     m_number = num;
     m_instNum = inst;
     m_val = val;
     m_onChange(m_index);
}

void Event::clear() {
     m_type = Event::Note;
     setEvent(Event::NoNote, Event::NoInst, Event::NoAmp);
}

void Event::setType(Type type) {
     // don't bother calling onChange slots if value is the same
     if (type == m_type)
	  return;
     m_type = type;
     m_onChange(m_index);
}

void Event::toggleType() {
     if (m_type == Event::Note) {
	  m_type = Event::ControlChange;
	  if (m_number == Event::NoNote)
	       m_number = 0;
     } else {
	  m_type = Event::Note;
     }
     m_onChange(m_index);
}

int Event::number() {
     return m_number;
}

void Event::setNumber(int num) {
     if (num == m_number)
	  return;
     if (num > 127)
	  num = 127;
     m_number = num;
     // call onChange slots
     m_onChange(m_index);
}

int Event::instNum() {
     return m_instNum;
}

void Event::setInstNum(int instNum) {
     if (instNum == m_instNum)
	  return;
     m_instNum = instNum;
     m_onChange(m_index);
}

int Event::val() {
     return m_val;
}

void Event::setVal(int val) {
     if (val == m_val)
	  return;
     if (val > 127)
	  val = 127;
     m_val = val;
     m_onChange(m_index);
}

int Event::effect() {
     return m_effect;
}

void Event::setEffect(int effect) {
     if (effect == m_effect)
	  return;
     m_effect = effect;
     m_onChange(m_index);
}

int Event::effectArg() {
     return m_effectArg;
}

void Event::setEffectArg(int effectArg) {
     if (effectArg == m_effectArg)
	  return;
     m_effectArg = effectArg;
     m_onChange(m_index);
}

string Event::noteStr() {
     switch (m_number) {
     case Event::NoNote:  
	  return "...";
     case Event::NoteOff: 
	  return "off";
     default:
	  ostringstream ns;
	  if (m_number > 108)
	       m_number = 108;
	  if (m_number < 0)
	       m_number = 0;
	  switch(m_number % 12) {
	  case 0: 
	       ns << "C-"; 
	       break;
	  case 1: 
	       ns << "C#"; 
	       break;
	  case 2: 
	       ns << "D-"; 
	       break;
	  case 3: 
	       ns << "D#"; 
	       break;
	  case 4: 
	       ns << "E-"; 
	       break;
	  case 5: 
	       ns << "F-"; 
	       break;
	  case 6: 
	       ns << "F#"; 
	       break;
	  case 7: 
	       ns << "G-"; 
	       break;
	  case 8: 
	       ns << "G#"; 
	       break;
	  case 9: 
	       ns << "A-"; 
	       break;
	  case 10: 
	       ns << "A#"; 
	       break;
	  case 11: 
	       ns << "B-"; 
	       break;
	  }
	  ns << setw(1) << m_number/12;
	  return ns.str();
     }
}

string Event::ccNumStr() {
     ostringstream s;
     s << "X" << hex << uppercase << setw(2) << setfill('0') << m_number;
     return s.str();
}

string Event::numStr() {
     if (m_type == Event::Note)
	  return noteStr();
     else
	  return ccNumStr();
}

string Event::instNumStr() {
     if (m_instNum == NoInst)
	  return "..";
     ostringstream s;
     s << hex << uppercase << setw(2) << setfill('0') << m_instNum + 1;
     return s.str();
}

string Event::valStr() {
     if (m_val == NoAmp)
	  return "..";
     ostringstream s;
     s << hex << uppercase << setw(2) << setfill('0') << m_val;
     return s.str();
}

string Event::effectStr() {
     if (m_effect == NoEffect)
	  return "...";
     ostringstream s;
     s << (char)m_effect \
       << hex << uppercase << setw(2) << setfill('0') << m_val;
     return s.str();
}

string Event::toString() {
     ostringstream s;
     s << numStr() << instNumStr() << valStr() << effectStr();
     return s.str();
}

void Event::fromString(const string &str) {
     setNumFromString(str.substr(0,3));
     setInstFromString(str.substr(3,2));
     setValFromString(str.substr(5,2));
     setEffectFromString(str.substr(7,2));
     m_onChange(m_index);
}

void Event::setNumFromString(string str) {
     if (str[0] == 'X')
	  setCcNumFromString(str);
     else
	  setNoteNumFromString(str);
}

void Event::setCcNumFromString(string str) {
     m_type = Event::ControlChange;
     istringstream is(str.substr(1,2));
     is >> hex >> m_number;
}

void Event::setNoteNumFromString(string str) {
     m_type = Event::Note;
     if (str == "...") {
	  m_number = Event::NoNote;
	  return;
     }
     string note = str.substr(0,2);
     if (note == "C-")
	  m_number = 0;
     if (note == "C#")
	  m_number = 1;
     if (note == "D-")
	  m_number = 2;
     if (note == "D#")
	  m_number = 3;
     if (note == "E-")
	  m_number = 4;
     if (note == "F-")
	  m_number = 5;
     if (note == "F#")
	  m_number = 6;
     if (note == "G-")
	  m_number = 7;
     if (note == "G#")
	  m_number = 8;
     if (note == "A-")
	  m_number = 9;
     if (note == "A#")
	  m_number = 10;
     if (note == "B-")
	  m_number = 11;
     istringstream is(str.substr(2,1));
     int octave=0;
     is >> octave;
     m_number += octave * 12;
}

void Event::setInstFromString(string str) {
     if (str == "..") {
	  m_instNum = Event::NoInst;
	  return;
     }
     istringstream is(str);
     is >> hex >> m_instNum;
     m_instNum--;
}

void Event::setValFromString(string str) {
     if (str == "..") {
	  m_val = Event::NoAmp;
	  return;
     }
     istringstream is(str);
     is >> hex >> m_val;
}

void Event::setEffectFromString(string str) {
}

signals2::connection Event::onChange(const OnChange::slot_type &callback) {
     return m_onChange.connect(callback);
}

int Event::index() {
     return m_index;
}

void Event::setIndex(int num) {
     m_index = num;
}
